﻿using HalconDotNet;
using MvCamCtrl.NET;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using static MvCamCtrl.NET.MyCamera;

namespace NDK.AcqDevice
{
    public class HikSDK : ICarmera
    {
        [System.Runtime.InteropServices.DllImport("winmm")]
        private static extern int timeGetTime();
        object obj = new object();
        private MyCamera m_MyCamera;
        string _SerialNumber = "";
        string _FilePath = "";
        cbOutputExdelegate cbImage;
        public string SerialNumber { get { return _SerialNumber; } }
        public string FilePath { get { return _FilePath; } }

        double _Exposure = 0;
        public double Exposure { get { return (double)(GetExposureTime() / 1000); } set { int nRet = m_MyCamera.MV_CC_SetFloatValue_NET("ExposureTime", (float)(value * 1000)); } }
        HImage GetImage { get; set; } = null;
        public int Height { get { return (int)GetHeight(); } set { int nRet = m_MyCamera.MV_CC_SetIntValue_NET("Height", (uint)value); } }
        public int Width { get { return (int)GetWidth(); } set { int nRet = m_MyCamera.MV_CC_SetIntValue_NET("Width", (uint)value); } }
        public ConcurrentQueue<HImage> Queuephoto { get; set; }
        public string CameraPixelFormat { get; set; } = "未连接";

        /// <summary>
        /// 构造方法中初始化相关数据
        /// </summary>
        /// <param name="baudRate">端口号</param>
        /// <param name="dataBits">数据位</param>
        public HikSDK()
        {

        }

        public bool CloseCamera()
        {
            int Res = 0;
            try
            {
                //Res += m_MyCamera.MV_CC_StopGrabbing_NET();
                Res += m_MyCamera.MV_CC_CloseDevice_NET();
                Res += m_MyCamera.MV_CC_DestroyDevice_NET();
            }
            catch (Exception ex)
            {
                Res = 1;
                writeLog($"关闭相机报错 {ex}", "海康相机报错");
            }
            return Res == 0;
        }

        private int StartCamera()
        {
            try
            {
                return m_MyCamera.MV_CC_StartGrabbing_NET();
            }
            catch (Exception ex)
            {
                writeLog($"打开相机报错 {ex}", "海康相机报错");
                return 1;
            }
        }

        MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
        public bool ConnectCamera(string SerialNumber, string FilePath, double exp)
        {
            try
            {
                _Exposure = exp;
                _SerialNumber = SerialNumber;
                _FilePath = FilePath;
                // ch:创建设备列表 || en: Create device list
                System.GC.Collect();
                bool BSearch = false;
                int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList);
                if (0 != nRet)
                {
                    MessageBox.Show("获取设备失败!!!");
                    return false;
                }
                MyCamera.MV_CC_DEVICE_INFO device = new MV_CC_DEVICE_INFO();
                // ch:在窗体列表中显示设备名 || Display the device'name on window's list
                for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
                {
                    device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                    if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                    {
                        MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
                        if (gigeInfo.chSerialNumber == SerialNumber)
                        {
                            BSearch = true;
                        }
                    }
                    else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                    {
                        MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                        if (usbInfo.chSerialNumber == SerialNumber)
                        {
                            BSearch = true;
                        }
                    }
                    if (BSearch)
                    {
                        if (m_MyCamera == null)
                        {
                            m_MyCamera = new MyCamera();
                            if (m_MyCamera == null) return false;
                        }
                        nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);
                        if (MV_OK != nRet)
                        {
                            return false;
                        }
                        nRet = m_MyCamera.MV_CC_OpenDevice_NET();
                        if (MV_OK != nRet)
                        {
                            m_MyCamera.MV_CC_DestroyDevice_NET();
                            //MessageBox.Show("打开设备失败");
                            return false;
                        }

                        // ch:取像素格式 | en:Get Pixel Format
                        MVCC_ENUMVALUE pcPixelFormat = new MVCC_ENUMVALUE();
                        nRet = m_MyCamera.MV_CC_GetEnumValue_NET("PixelFormat", ref pcPixelFormat);
                        if (MV_OK != nRet)
                        {
                            MessageBox.Show("获取像素格式失败");
                            return false;
                        }

                        // ch:设置bitmap像素格式
                        if ((Int32)MvGvspPixelType.PixelType_Gvsp_Undefined == (Int32)pcPixelFormat.nCurValue)
                        {
                            MessageBox.Show("Unknown Pixel Format!失败");
                            return false;
                        }
                        else if (IsMono((MvGvspPixelType)pcPixelFormat.nCurValue))
                        {
                            CameraPixelFormat = "Mono";
                            m_enBitmapPixelFormat = PixelFormat.Format8bppIndexed;
                        }
                        else
                        {
                            CameraPixelFormat = "Color";
                            m_enBitmapPixelFormat = PixelFormat.Format24bppRgb;
                        }

                        if (null != bitmap)
                        {
                            bitmap.Dispose();
                            bitmap = null;
                        }
                        bitmap = new Bitmap((Int32)(GetWidth()), (Int32)(GetHeight()), m_enBitmapPixelFormat);

                        // ch:Mono8格式，设置为标准调色板 | en:Set Standard Palette in Mono8 Format
                        if (PixelFormat.Format8bppIndexed == m_enBitmapPixelFormat)
                        {
                            ColorPalette palette = bitmap.Palette;
                            for (int ii = 0; ii < palette.Entries.Length; ii++)
                            {
                                palette.Entries[ii] = Color.FromArgb(ii, ii, ii);
                            }
                            bitmap.Palette = palette;
                        }

                        // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
                        if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                        {
                            int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET();
                            if (nPacketSize > 0)
                            {
                                nRet = m_MyCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                                if (nRet != MyCamera.MV_OK)
                                {
                                    MessageBox.Show("Set Packet Size failed!");
                                }
                            }
                            else
                            {
                                MessageBox.Show("Get Packet Size failed!");
                            }
                        }
                        cbImage = new MyCamera.cbOutputExdelegate(ImageCallBack);
                        m_MyCamera.MV_CC_RegisterImageCallBackEx_NET(cbImage, IntPtr.Zero);//修改
                        // ch:设置采集连续模式 | en:Set Continues Aquisition Mode
                        m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
                        m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);
                        SetTriggerMode(TriggerMode.Single);
                        StartCamera();
                        Exposure = _Exposure;
                        Queuephoto = new ConcurrentQueue<HImage>();
                        return true;
                    }
                }
                if (!BSearch)
                {
                    MessageBox.Show("没有搜索到设备");
                    return false;
                }
                else { return true; }
            }
            catch (Exception ex)
            {
                MessageBox.Show("查找海康相机时发生异常：" + ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }
        PixelFormat m_enBitmapPixelFormat = PixelFormat.DontCare;

        private static Boolean IsMono(MvGvspPixelType enPixelType)
        {
            switch (enPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
                    return true;
                case MvGvspPixelType.PixelType_Gvsp_Mono1p:
                case MvGvspPixelType.PixelType_Gvsp_Mono2p:
                case MvGvspPixelType.PixelType_Gvsp_Mono4p:
                case MvGvspPixelType.PixelType_Gvsp_Mono8:
                case MvGvspPixelType.PixelType_Gvsp_Mono8_Signed:
                case MvGvspPixelType.PixelType_Gvsp_Mono10:
                case MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
                case MvGvspPixelType.PixelType_Gvsp_Mono12:
                case MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
                case MvGvspPixelType.PixelType_Gvsp_Mono14:
                case MvGvspPixelType.PixelType_Gvsp_Mono16:
                    return true;
                default:
                    return false;
            }
        }

        // ch:取流回调函数 | en:Aquisition Callback Function        
        private void ImageCallBack(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
        {
            lock (obj)
            {
                try
                {
                    // ch:抓取的帧数 | en:Aquired Frame Number 
                    GetImage = new HImage();
                    if (CameraPixelFormat == "Color")
                    {
                        GetImage.GenImageInterleaved(pData, "bgr", pFrameInfo.nWidth, pFrameInfo.nHeight, -1, "byte",
                                         pFrameInfo.nWidth, pFrameInfo.nHeight, 0, 0, -1, 0);
                    }
                    else if (CameraPixelFormat == "Mono")
                    {
                        GetImage.GenImage1("byte", pFrameInfo.nWidth, pFrameInfo.nHeight, pData);
                    }
                    Queuephoto.Enqueue(GetImage);
                }
                catch (Exception ex)
                {
                    writeLog($"回调函数报错 {ex}", "海康相机报错");
                }
            }
        }

        /// <summary>
        /// 延时
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public void Sleep(int time)
        {
            try
            {
                long interval;
                int oldTime = timeGetTime();
                do
                {
                    System.Windows.Forms.Application.DoEvents();
                    interval = timeGetTime() - oldTime;
                    if (interval < 0) interval = (long)Math.Pow(2, 32) - Math.Abs(interval);
                } while (interval < time);
            }
            catch (System.Exception ex) { MessageBox.Show(ex.Message); }
        }

        public HImage GrabImage(double Ep, bool IsChangeGetImageMode = false)
        {
            GetImage = null;
            try
            {
                if (!IsChangeGetImageMode)
                {
                    if (_Exposure != Ep)
                        Exposure = Ep;
                    Queuephoto = new ConcurrentQueue<HImage>();
                    m_MyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
                    Stopwatch soapTime = new Stopwatch();
                    soapTime.Restart();
                    do
                    {
                        if (soapTime.ElapsedMilliseconds > 2000)
                            return GetImage;
                        Sleep(10);
                    } while (Queuephoto.Count == 0);
                    if (Queuephoto.TryDequeue(out HImage hImage))
                        GetImage = hImage;
                }
                else
                {
                    m_MyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
                    Sleep(Convert.ToInt32(Math.Round(Ep)) + 300);
                }
            }
            catch (Exception ex)
            {
                writeLog($"拍照片报错 {ex}", "海康相机报错");
            }
            return GetImage;
        }

        public static void writeLog(string str, string DirFileName, bool IsShow = false)
        {
            FileInfo fi = new FileInfo($"D:\\Log\\{DirFileName}\\{DateTime.Now.ToString("yyyyMMdd")}.txt");
            if (!Directory.Exists(fi.DirectoryName))
            {
                Directory.CreateDirectory(fi.DirectoryName);
            }

            using (StreamWriter sw = new StreamWriter(fi.FullName, true))
            {
                sw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff") + ":     " + str);
                if (IsShow)
                {
                    sw.WriteLine("---------------------------------------------------------");
                }
                sw.Flush();
                sw.Close();
            }
        }

        public float GetExposureTime()
        {
            float value = 0;
            int iRetryCount = 0;
            bool bDone = false;
            while (!bDone && (iRetryCount < 3))
            {
                try
                {
                    MVCC_FLOATVALUE exp = new MVCC_FLOATVALUE();
                    int nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ExposureTime", ref exp);
                    if (nRet != 0)
                    {
                        //Relink();
                        iRetryCount++;
                    }
                    else
                    {
                        bDone = true;
                        value = exp.fCurValue;
                    }

                }
                catch
                {
                    iRetryCount++;
                }
            }
            return value;
        }

        public long GetHeight()
        {
            long value = 0;
            int iRetryCount = 0;
            bool bDone = false;
            while (!bDone && (iRetryCount < 3))
            {
                try
                {
                    MVCC_INTVALUE pcHeight = new MVCC_INTVALUE();
                    int nRet = m_MyCamera.MV_CC_GetIntValue_NET("Height", ref pcHeight);
                    if (nRet != 0)
                    {
                        //Relink();
                        iRetryCount++;
                    }
                    else
                    {
                        bDone = true;
                        value = pcHeight.nCurValue;
                    }

                }
                catch
                {
                    iRetryCount++;
                }
            }
            return value;
        }

        public long GetWidth()
        {
            long value = 0;
            int iRetryCount = 0;
            bool bDone = false;
            while (!bDone && (iRetryCount < 3))
            {
                try
                {
                    MVCC_INTVALUE pcWidth = new MVCC_INTVALUE();
                    int nRet = m_MyCamera.MV_CC_GetIntValue_NET("Width", ref pcWidth);
                    if (nRet != 0)
                    {
                        //Relink();
                        iRetryCount++;
                    }
                    else
                    {
                        bDone = true;
                        value = pcWidth.nCurValue;
                    }

                }
                catch
                {
                    iRetryCount++;
                }
            }
            return value;
        }

        /// <summary>
        /// 设置触发
        /// </summary>
        /// <param name="mode"></param>
        private void SetTriggerMode(TriggerMode mode)
        {
            int nRet = 0;
            switch (mode)
            {
                case TriggerMode.Continuse:
                    nRet += m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);
                    if (nRet != 0)
                    {
                        MessageBox.Show("Set TriggerMode Fail");
                        return;
                    }
                    break;

                case TriggerMode.Single:
                    nRet += m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
                    nRet += m_MyCamera.MV_CC_SetEnumValueByString_NET("TriggerSource", "Software");
                    if (nRet != 0)
                    {
                        MessageBox.Show("Set TriggerMode Fail");
                        return;
                    }
                    break;
            }
        }

        IntPtr pBufForConvert = IntPtr.Zero;
        MV_FRAME_OUT stFrameOut = new MV_FRAME_OUT();
        Bitmap bitmap = null;
        public void ReadBufferToBitmap()//读取相机buffer并生成Bmp格式的图像
        {
            lock (obj)
            {
                try
                {
                    int nRet;
                    DateTime date = DateTime.Now;
                    do
                    {
                        nRet = m_MyCamera.MV_CC_GetImageBuffer_NET(ref stFrameOut, 1000);
                        if ((DateTime.Now - date).Seconds > 5)
                        {
                            MessageBox.Show("相机拍照失败");
                            return;
                        }
                        Sleep(10);
                    } while (nRet != MV_OK);

                    MyCamera.MvGvspPixelType enType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Undefined;
                    uint nChannelNum = 0;
                    if (CameraPixelFormat == "Color")
                    {
                        enType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
                        nChannelNum = 3;
                    }
                    else if (CameraPixelFormat == "Mono")
                    {
                        enType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
                        nChannelNum = 1;
                    }
                    else
                    {
                        Console.WriteLine("Don't need to convert!");
                    }

                    if (enType != MyCamera.MvGvspPixelType.PixelType_Gvsp_Undefined)
                    {
                        if (pBufForConvert == IntPtr.Zero)
                        {
                            pBufForConvert = Marshal.AllocHGlobal((int)(stFrameOut.stFrameInfo.nWidth * stFrameOut.stFrameInfo.nHeight * nChannelNum));
                        }
                        MV_PIXEL_CONVERT_PARAM stConvertPixelParam = new MV_PIXEL_CONVERT_PARAM();
                        stConvertPixelParam.nWidth = stFrameOut.stFrameInfo.nWidth;
                        stConvertPixelParam.nHeight = stFrameOut.stFrameInfo.nHeight;
                        stConvertPixelParam.pSrcData = stFrameOut.pBufAddr;
                        stConvertPixelParam.nSrcDataLen = stFrameOut.stFrameInfo.nFrameLen;
                        stConvertPixelParam.enSrcPixelType = stFrameOut.stFrameInfo.enPixelType;
                        stConvertPixelParam.enDstPixelType = enType;
                        stConvertPixelParam.pDstBuffer = pBufForConvert;
                        stConvertPixelParam.nDstBufferSize = (uint)(stFrameOut.stFrameInfo.nWidth * stFrameOut.stFrameInfo.nHeight * nChannelNum);
                        m_MyCamera.MV_CC_ConvertPixelType_NET(ref stConvertPixelParam);
                        if (MyCamera.MV_OK != nRet)
                        {
                            MessageBox.Show("取图失败");
                            return;
                        }

                        if (CameraPixelFormat == "Color")
                        {
                            GetImage = new HImage();
                            GetImage.GenImageInterleaved(
                                stConvertPixelParam.pSrcData,
                                "bgr",
                                stConvertPixelParam.nWidth, stConvertPixelParam.nHeight,
                                -1, "byte",
                                stConvertPixelParam.nWidth, stConvertPixelParam.nHeight,
                                0, 0, -1, 0);
                        }
                        else if (CameraPixelFormat == "Mono")
                        {
                            GetImage = new HImage();
                            GetImage.GenImage1("byte", stConvertPixelParam.nWidth, stConvertPixelParam.nHeight, stConvertPixelParam.pSrcData);
                            //// ch:保存Bitmap数据 | en:Save Bitmap Data
                            //BitmapData m_pcBitmapData = bitmap.LockBits(new Rectangle(0, 0, stConvertPixelParam.nWidth, stConvertPixelParam.nHeight), ImageLockMode.ReadWrite, bitmap.PixelFormat);
                            //// ch:将图像数据保存到本地文件 | en:Save image data to local file
                            //byte[] data = new byte[stConvertPixelParam.nDstLen];
                            //Marshal.Copy(stConvertPixelParam.pDstBuffer, data, 0, (int)stConvertPixelParam.nDstLen);
                            //m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameOut);
                            ////HOperatorSet.GenImage1(out HObject image, "byte", bitmap.Width, bitmap.Height, m_pcBitmapData.Scan0);
                            ////GetImage = new HImage(image);
                            //bitmap.Save("D:\\234.bmp", ImageFormat.Bmp);
                            //bitmap.UnlockBits(m_pcBitmapData);
                        }
                        else
                        {
                            Console.WriteLine("Don't need to convert!");
                        }


                        //// ch:保存Bitmap数据 | en:Save Bitmap Data
                        //BitmapData m_pcBitmapData = bitmap.LockBits(new Rectangle(0, 0, stConvertPixelParam.nWidth, stConvertPixelParam.nHeight), ImageLockMode.ReadWrite, bitmap.PixelFormat);
                        //// ch:将图像数据保存到本地文件 | en:Save image data to local file
                        //byte[] data = new byte[stConvertPixelParam.nDstLen];
                        //Marshal.Copy(stConvertPixelParam.pDstBuffer, data, 0, (int)stConvertPixelParam.nDstLen);
                        //bitmap.UnlockBits(m_pcBitmapData);
                        //m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameOut);

                    }
                }
                catch
                {

                }
            }
        }
        enum TriggerMode
        {
            Single,
            Continuse,
        }
    }
}
